package com.sright.bcard.common.service.share.mvcresolver;

import com.alibaba.fastjson.JSONObject;
import com.sright.bcard.common.service.share.utils.CharsetUtils;
import com.sright.bcard.common.service.share.utils.InputStreamTool;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpMethod;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.lang.Nullable;
import org.springframework.ui.ModelMap;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Type;
import java.net.URLDecoder;

public class RequestBodyArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(BodyFiled.class) || parameter.hasParameterAnnotation(BodyContent.class);
    }

    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
        parameter = parameter.nestedIfOptional();
        String arg = this.readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
        JSONObject object = null;
        ModelMap model = mavContainer.getModel();
        if (StringUtils.isEmpty(arg)) {
            arg = "{}";
        }
        //
        if (!StringUtils.isEmpty(arg) && !model.containsAttribute("bodyContent")) {
            if (arg.startsWith("{")) {
                object = JSONObject.parseObject(arg);
            } else {
                object = new JSONObject();
                String[] items = arg.split("&");
                for (String item : items) {
                    int i = item.indexOf("=");
                    if (i > 1) {
                        object.put(item.substring(0, i), item.substring(i + 1));
                    }
                }
            }
            mavContainer.getModel().addAttribute("bodyContent", object);
        } else {
            object = (JSONObject) mavContainer.getModel().getAttribute("bodyContent");
        }
        String name = parameter.getParameterName();
        Class<?> clazz = parameter.getParameterType();
        if (parameter.hasParameterAnnotation(BodyFiled.class)) {
            BodyFiled annotation = parameter.getParameterAnnotation(BodyFiled.class);
            String annoValue = annotation.value();
            if (!StringUtils.isEmpty(annoValue)) {
                name = annoValue;
            }
            if (clazz.equals(Integer.class)) {
                return object.getInteger(name);
            } else if (clazz.equals(Short.class)) {
                return object.getShort(name);
            } else if (clazz.equals(Long.class)) {
                return object.getLong(name);
            } else if (clazz.equals(Double.class)) {
                return object.getDouble(name);
            } else if (clazz.equals(Boolean.class)) {
                return object.getBoolean(name);
            } else if (clazz.equals(Float.class)) {
                return object.getFloat(name);
            } else if (clazz.equals(String.class)) {
                return object.getString(name);
            }
        } else if (parameter.hasParameterAnnotation(BodyContent.class)) {
            BodyContent annotation = parameter.getParameterAnnotation(BodyContent.class);
            return object.toJavaObject(clazz);
        }
        return object.get(name);

    }

    protected String readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter, Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
        HttpServletRequest servletRequest = (HttpServletRequest) webRequest.getNativeRequest(HttpServletRequest.class);
        Assert.state(servletRequest != null, "No HttpServletRequest");
        ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
        HttpMethod method = inputMessage.getMethod();
        if (HttpMethod.GET.equals(method)) {
            String query = servletRequest.getQueryString();
            if (StringUtils.isEmpty(query)) {
                query = URLDecoder.decode(query, CharsetUtils.UTF8);
            }
            return query;
        } else {
            return InputStreamTool.convertStreamToString(inputMessage.getBody(), CharsetUtils.UTF8);
        }
    }
}